--[[
模块名称：prot808
模块功能�?808协议实现
模块最后修改时间：2019.04.18
]]

local lpack = require"pack"

module(...,package.seeall)

local slen,sbyte,ssub,sgsub,schar,srep,smatch,sgmatch = string.len,string.byte,string.sub,string.gsub,string.char,string.rep,string.match,string.gmatch
local sformat = string.format
local bpack = lpack.pack
local bunpack = lpack.unpack

--up
MSG_REGISTER = 0x0100
MSG_HEART = 0x0002
MSG_UNREGISTER = 0x0003
MSG_AUTH = 0x0102
MSG_POSITION = 0x0200
MSG_BATCH_POSITION = 0x0704

MSG_GENERAL_RESPONSE = 0x0001

--down
MSG_SERVER_RESPONSE = 0x8001
MSG_SERVER_REGIST_RESPONSE = 0x8100
MSG_SERVER_CONTROL_DEV = 0x8105
MSG_SERVER_CONTROL_CAR = 0x8500

local imei -- 命令头包含IMEI,在第一次获取以后做缓存
local get
local sendFunc,sucFunc

local function print(...)
	_G.print("protoair808",...)
end

function bcd(d,n)
	local l = slen(d or "")
	local num
	local t = {}

	s = d
	if l < 2*n then
		s = srep("0",2*n-l) .. s
	elseif l > 2*n then
		s = ssub(s,l-2*n+1,-1)
	end

	for i=1,2*n,2 do
		num = tonumber(ssub(s,i,i+1),16)
		table.insert(t,num)
	end

	local s = schar(_G.unpack(t))
	return s
end

local function stringProc(s,el)
	local ss = s
	l = slen(s)

	if l < el then
		ss = s .. srep("\0",el-l) 
	elseif l > el then
		ss = ssub(s,1,el)
	end
	return ss
end

function print_r ( t )  
    local print_r_cache={}
    local function sub_print_r(t,indent)
        if (print_r_cache[tostring(t)]) then
            print(indent.."*"..tostring(t))
        else
            print_r_cache[tostring(t)]=true
            if (type(t)=="table") then
                for pos,val in pairs(t) do
                    if (type(val)=="table") then
                        print(indent.."["..pos.."] => "..tostring(t).." {")
                        sub_print_r(val,indent..string.rep(" ",string.len(pos)+8))
                        print(indent..string.rep(" ",string.len(pos)+6).."}")
                    elseif (type(val)=="string") then
                        print(indent.."["..pos..'] => "'..val..'"')
                    else
                        print(indent.."["..pos.."] => "..tostring(val))
                    end
                end
            else
                print(indent..tostring(t))
            end
        end
    end
    if (type(t)=="table") then
        print(tostring(t).." {")
        sub_print_r(t,"  ")
        print("}")
    else
        sub_print_r(t,"  ")
    end
    print()
end

table.print = print_r

-----------------------------------------------------------------------------------------------------------------------

local function cmdName(cmd)
	if cmd == MSG_REGISTER then
		return "MSG_REGISTER"
	elseif cmd == MSG_HEART then
		return "MSG_HEART"
	elseif cmd == MSG_UNREGISTER then
		return "MSG_UNREGISTER"
	elseif cmd == MSG_AUTH then
		return "MSG_AUTH"
	elseif cmd == MSG_POSITION then
		return "MSG_POSITION"
	elseif cmd == MSG_BATCH_POSITION then
		return "MSG_BATCH_POSITION"
	elseif cmd == MSG_GENERAL_RESPONSE then
		return "MSG_GENERAL_RESPONSE"
	--down
	elseif cmd == MSG_SERVER_RESPONSE then
		return "MSG_SERVER_RESPONSE"
	elseif cmd == MSG_SERVER_REGIST_RESPONSE then
		return "MSG_SERVER_REGIST_RESPONSE"
	elseif cmd == MSG_SERVER_CONTROL_DEV then
		return "MSG_SERVER_CONTROL_DEV"
	elseif cmd == MSG_SERVER_CONTROL_CAR then
		return "MSG_SERVER_CONTROL_CAR"
	end
	return "UNKOWN_CMD"
end


local function computeCrc(indata)
    local r = 0
    for i = 1, slen(indata) do
        local charcode = sbyte(indata, i, i)
		r = bit.bxor(r, charcode)
    end
    return r
end

local MSG_INDEX = 0xffff
local function genMsgIndex()
    MSG_INDEX = MSG_INDEX + 1
	MSG_INDEX = bit.band(MSG_INDEX,0xffff)
	return MSG_INDEX
end

local function genMsgParam(len, encT, totalMsgSplit)
	len = bit.band(len,0x3ff)
	encT = bit.band(encT,0x07)
	encT = bit.lshift(encT, 10)
	splitP = 0
	if (totalMsgSplit > 1) then
	    splitP = 1
	end
	splitP = bit.lshift(splitP, 13)
	return len + encT + splitP
end

local function encLatLng(gps)
    -- print("xxxxx", gps)
	-- print("xxxxx", tonumber(gps))
	if gps == nil then
		gps = "0.0" 
	end
    local integer,fraction = smatch(gps,"(%d+)%.(%d+)")
    if (fraction == nil) then
        return 0
    end
    strL = string.len(fraction)
    str = ssub(fraction,1,-1-(strL-6))
    -- print("xxxxx", tonumber(integer))
    -- print("xxxxx", tonumber(fraction))
    local ret = tonumber(sformat("%s%s", integer, str))
    -- print("xxxxx", ret)
    return ret
end

function getPhoneEx()
	imei = _G.TEST_IMEI
	if imei == nil or imei == "" then
		ii = get("IMEI") --"869300038441535"
		log.info("--zbb--imei:", ii)
		if ii ~= nil then
			-- imei = "13"..ssub(ii,slen(ii)-9+1,-1)
			imei = "13"..ssub(ii,slen(ii)-10+1,-2)
		end
	end
	log.error("--zbb--phone:", imei)
	return imei
end
-----------------------------------------------------------------------------

local function packMsg(msgtype, msg)
    local total = 1
    local cur = 0

    local msglen = slen(msg)
	-- if (total > 1) then
    --     msglen = msglen + 4
    -- end
    -- msglen = msglen + 2
    
    local msgparam = genMsgParam(msglen, 0, total)
    local msgs = 0
	
	-- local imei111 = get("IMEI")
	-- log.error("--zbb--imei111:", imei111)
	-- imei2222 = bcd(get("IMEI"),6)
	-- log.error("--zbb--imei2222:", string.toHex(imei2222))
	
	if not imei or imei == "" then 
		imei = bcd(getPhoneEx(),6) 
	end
	log.error("--zbb--imei:", string.toHex(imei))
	local a = bpack(">H2AH", msgtype, msgparam, imei, genMsgIndex())
	local tbl = {}
    table.insert(tbl, a)
	if (total > 1) then
        b = bpack(">H2", total, cur)
        table.insert(tbl, b)
	end
    if (msg ~= nil) then
        table.insert(tbl, msg)
    end
    return table.concat(tbl)
end

function packAll(t,indata)
	h1 = packMsg(t, indata)
	
	local r = computeCrc(h1)
	--print("rr:" .. r)
	indatacrc = h1 .. string.format("%c", r);
    --print(hex2str(indatacrc))

	local str = sformat("%c", 0x7e)
	for i = 1, slen(indatacrc) do
        local charcode = string.byte(indatacrc, i, i);
		if (charcode == 0x7d) then
		    str = str .. string.format("%c%c", 0x7d, 0x01);
		elseif (charcode == 0x7e) then
		    str = str .. string.format("%c%c", 0x7d, 0x02);
		elseif (charcode == 0x00) then
		    str = str .. string.format("\0");
		else
            str = str .. string.format("%c", charcode);
		end
    end
	str = str .. sformat("%c", 0x7e);
	return str
end

function pack(t,...)

	local function empty(d)
		return true
	end

	local function regist()
		local provID = 0
		local cityID = 0
		local manufacturerId = "ccwcc" --5
		local terminalType = "ccw-z01s-201805"
		local terminalId = "1234567"
		local color = 0
		local licensePlate = "2029487246592"
		-- ss=stringProc(terminalType,20)
		-- log.error("--zbb--ss:", string.toHex(ss))
		-- start = bpack(">H2", provID, cityID)..stringProc(manufacturerId,5)
		-- log.error("--zbb--ss:", start)
		-- start = start .. stringProc(terminalType,20)..stringProc(terminalId,20)
		-- log.error("--zbb--ss:", start)
		-- return start..bpack(">b",color)..licensePlate
		return bpack(">H2AAAbA", provID, cityID, stringProc(manufacturerId,5), stringProc(terminalType,20), stringProc(terminalId,20), color, licensePlate)
	end

	local function auth(a)
		return a
	end

	local function heart()
		return ""
	end

	local function genRes(msgIndexFromServer, msgType, result)
		--log.info("--zwb--protocal.genResponseMsg:", msgIndexFromServer, msgType, result)
		return bpack(">H2b", msgIndexFromServer, msgType, result)
	end

	local function loc(gpsStat,devStat,adcr)
		-- local v = misc.getVbatt()
		-- log.info("---getVbatt mv:", v)

		local status = devStat.acc -- acc 0)
					 + (gpsStat.fix and 1 or 0) * 0x2 -- gps valid
					 + (gpsStat.lngType == "S" and 1 or 0) * 0x4
					 + (gpsStat.latType == "W" and 1 or 0) * 0x8
					 + (devStat.block) * 0x400 --10
		local alarm = 0
		            + devStat.dismantle * 0x100 --8
		log.error("--zbb--protoair808.loc status alarm:", status, alarm)
		-- a = nil
		-- string.toHex(a)

		local tbl = {}
		a = bpack(">I2",alarm,status)
		table.insert(tbl, a)

		if (gpsStat.lat ~= "" and gpsStat.lng ~= "") then
			b = bpack(">I2", encLatLng(gpsStat.lat), encLatLng(gpsStat.lng))
			table.insert(tbl, b)
		end
		d = bpack(">H3", gpsStat.alt, gpsStat.spd * 10, gpsStat.cog)
		table.insert(tbl, d)

		utcTime = gpsStat.time
		-- log.info("---updateGps utcTime:", #utcTime)
		table.print( utcTime )
		if (utcTime ~= nil) then
			year = utcTime["year"]
			year = ssub(year,3,-1)
			-- log.info("---updateGps utcTime year:", year)
			time = sformat("%02d%02d%02d%02d%02d%02d", year, utcTime["month"], utcTime["day"], utcTime["hour"], utcTime["min"], utcTime["sec"])
			local tttt = bcd(time,6)
			log.info("---updateGps utcTime2:", string.toHex(tttt))
			table.insert(tbl, tttt)
		end
		-- ret = table.concat(tbl)
		-- log.info("---updateGps ret:", string.toHex(ret))
		return table.concat(tbl)
	end

	local function locbatch(msgs)
		local count = #msgs

		local tbl = {}
		a = bpack(">Hb",count,1) --位置数据类型 0：正常位置批量汇报，1：盲区补�?
		table.insert(tbl, a)

		for i=1,count do
			local msg = msgs[i]
			log.info("--zbb--protoair808.locbatch:",i,string.toHex(msg))
			
			local len = slen(msg)
			b = bpack(">H",len)
			table.insert(tbl, b)
			table.insert(tbl, msg)
		end
		return table.concat(tbl)
	end

    local procer = {
		[MSG_REGISTER] = regist,
		[MSG_AUTH] = auth,
		[MSG_GENERAL_RESPONSE] = genRes,
		[MSG_POSITION] = loc,
		[MSG_BATCH_POSITION] = locbatch,
		[MSG_HEART] = heart,
	}

	local s = procer[t](...)
	print("--zbb--pack",cmdName(t),string.toHex(s))
	return s
end

function setInitFunc(sfunc,sucfunc)
	sendFunc = sfunc
	sucFunc = sucfunc
end

function init()
	d = pack(MSG_REGISTER)
	sendFunc(MSG_REGISTER,d)
    return 0
end

function heart()
	-- log.info("--zbb--32960.heart")
	d = pack(MSG_HEART)
	sendFunc(MSG_HEART,d)
end

local function authReq(a)
	d = pack(MSG_AUTH,a)
	sendFunc(MSG_AUTH,d)
end

local function genResponseMsg(msgIndexFromServer, msgType, result)
	d = pack(MSG_GENERAL_RESPONSE,msgIndexFromServer, msgType, result)
	sendFunc(MSG_GENERAL_RESPONSE,d)
end

function loc(t,stat,adcr)
	d = pack(MSG_POSITION,t,stat,adcr)
	if sendFunc ~= nil then sendFunc(MSG_POSITION,d) end
end

function locbatch(msgs)
	d = pack(MSG_BATCH_POSITION,msgs)
	if sendFunc ~= nil then sendFunc(MSG_BATCH_POSITION,d) end
end

---------------------------------------------------------------------------------------
local function pa(b)
    return sformat("%d%d",b/16,b%16)
end

--[[
函数名：unpack
功能  ：根据ptotobuffer协议进行封包
参数  �?
		s:需要解析字符串
返回值：解包字符�?
]]
function unpack(s)
	local packet = {}
			
	local function registRes(data,msgIndexFromServer)
		if slen(data) > 0 then
			extpox1,msgIndexFromDevice,result=bunpack(data,">Hb")
			log.info("--zbb--protacal808.registRes:",sformat("result:%d, msgIndexFromDevice:%d",result,msgIndexFromDevice))
			auth = ssub(data, 4, -1)
			log.info("--zbb--protacal808.registRes auth:",auth)
			authReq(auth)
		end
	end
			
	local function generalRes(data,msgIndexFromServer)
		if slen(data) > 0 then						
			extpox1,msgIndexFromDevice,msgTypeFromDevice,result=bunpack(data,">HHb")
			log.info("--zbb--protacal808.generalRes:",
			    sformat("result:%d, msgIndexFromDevice:%d, msgTypeOrgCmd:%s(0x%04X)",result,msgIndexFromDevice,cmdName(msgTypeFromDevice),msgTypeFromDevice))
			if msgTypeFromDevice == MSG_AUTH then
				sucFunc()
			end
		end
	end
	
	local function control(data,msgIndexFromServer)
		if slen(data) > 0 then
			extpoz,controlId=bunpack(data,">b")
			param = ssub(data, extpoz, -1)
			log.info("--zbb--protoair808.control:",controlId,param)
			if controlId == 0 or controlId == 0x65 then
				pincontrol.setBlockWithNvm(false)
				genResponseMsg(msgIndexFromServer, MSG_SERVER_CONTROL_DEV, 0)
				sys.publish("MY_DEV_EVENT")
			elseif controlId == 1 or controlId == 0x64 then
				pincontrol.setBlockWithNvm(true)
				genResponseMsg(msgIndexFromServer, MSG_SERVER_CONTROL_DEV, 0)
				sys.publish("MY_DEV_EVENT")
			end
		end
	end
	
	local procer = {
		[MSG_SERVER_REGIST_RESPONSE] = registRes,
		[MSG_SERVER_RESPONSE] = generalRes,
		[MSG_SERVER_CONTROL_CAR] = control,
		[MSG_SERVER_CONTROL_DEV] = control,
	}

	local hexStr = string.toHex(s)
	-- hexStr = "7E80010005575021921749007D010217020000047E"
    -- log.error("--zbb--protoair808.unpack hexStr1=",hexStr)
    hexStr = sgsub(hexStr, "(7D02)", "7E")
    hexStr = sgsub(hexStr, "(7D01)", "7D")
    -- log.error("--zbb--protoair808.unpack hexStr2=",hexStr)

    local data1 = string.fromHex(hexStr)
    local data2 = ssub(data1, 2, -2)
    local r = computeCrc(data2)
    if r ~= 0 then
        log.error("--zbb--protoair808.unpack error crc=",r)
        return nil
    end
    -- extpoz,msgType,msgLen,b1,b2,b3,b4,b5,b6,msgIndexFromServer,ptotal,pindex=bunpack(data2,">HHb6HHH")
    extpoz,msgType,msgLen,b1,b2,b3,b4,b5,b6,msgIndexFromServer=bunpack(data2,">HHb6H")

    local imeiTT = sformat("%s%s%s%s%s%s", pa(b1), pa(b2), pa(b3), pa(b4), pa(b5), pa(b6))
    -- log.info("--zwb--protacal.parseMsg imei:",imei)
    local imeiLocal = string.toHex(imei)
    if imeiTT ~= imeiLocal then
        log.error("--zbb--protoair808.parseMsg imei error:", imeiTT, imeiLocal)
        return nil
    end

    local data3 = ssub(data2,extpoz,extpoz + msgLen - 1)
    log.info("--zbb--protoair808.parseMsg:",sformat("%s(0x%04X), serverIndex:%d",cmdName(msgType),msgType,msgIndexFromServer),string.toHex(data3))
	
	packet.id = msgType
	if procer[msgType] ~= nil then procer[msgType](data3,msgIndexFromServer) end
	return packet or nil
end

function reget(id)
	get = id
end
